﻿using System;
using System.IO;
using System.Linq;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;

namespace SomoDll
{
    public class MyTool_Zip
    {
        #region unzip
        /**/
        /// <summary>
        /// 解压功能(解压压缩文件到指定目录)
        /// </summary>
        /// <param name="fileToUnZip">待解压的文件</param>
        /// <param name="toDirectoryPath">指定解压目标目录</param>
        /// <param name="zipPassword">压缩密码</param>
        /// <param name="zipFilters">过滤的文件后缀列表</param>
        public static void UnZip(string fileToUnZip, string toDirectoryPath, string zipPassword, string[] zipFilters)
        {
            if (!File.Exists(fileToUnZip))
            {
                return;
            }

            if (!Directory.Exists(toDirectoryPath))
            {
                Directory.CreateDirectory(toDirectoryPath);
            }


            int zipFileCount = 0;

            while (true)
            {
                FileStream fileStream = File.OpenRead(fileToUnZip);
                using (fileStream)
                {
                    ZipInputStream s = new ZipInputStream(fileStream);
                    if (zipPassword != "")
                        s.Password = zipPassword;
                    for (int i = 0; i < zipFileCount; i++)
                        s.GetNextEntry();
                    zipFileCount++;

                    ZipEntry theEntry = null;
                    try
                    {
                        theEntry = (ZipEntry)s.GetNextEntry().Clone();
                    }
                    catch
                    {
                        theEntry = null;
                    }

                    if (theEntry == null)
                    {
                        break;
                    }

                    if (!string.IsNullOrEmpty(theEntry.Name))
                    {
                        string fileName = toDirectoryPath + theEntry.Name;

                        //判断文件路径是否是文件夹
                        if (theEntry.IsDirectory)
                        {
                            DirectoryInfo dir = new DirectoryInfo(fileName);
                            if (!dir.Exists)
                                dir.Create();
                        }
                        else
                        {
                            FileInfo fl = new FileInfo(fileName);
                            if (zipFilters != null && zipFilters.Contains(fl.Extension))
                            {
                                continue;
                            }

                            FileStream streamWriter = File.Create(fileName);
                            var dataSize = 2048;
                            byte[] data = new byte[dataSize];
                            while (true)
                            {
                                dataSize = s.Read(data, 0, data.Length);
                                if (dataSize > 0)
                                    streamWriter.Write(data, 0, dataSize);
                                else
                                    break;
                            }
                            streamWriter.Flush();
                            streamWriter.Close();
                            streamWriter.Dispose();
                        }
                    }
                }

            }
        }

        #endregion

        #region zip

        /**/

        /// <summary>
        /// 递归压缩文件夹方法
        /// </summary>
        /// <param name="folderToZip"></param>
        /// <param name="s"></param>
        /// <param name="parentFolderName"></param>
        /// <param name="filterExtensions"></param>
        private static bool ZipFileDictory(string folderToZip, ZipOutputStream s, string parentFolderName, string[] filterExtensions)
        {
            bool res = true;
            ZipEntry entry = null;
            FileStream fs = null;
            Crc32 crc = new Crc32();

            try
            {
                //创建当前文件夹
                if (folderToZip != _dirMain)
                {
                    entry = new ZipEntry(Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/"));  //加上 “/” 才会当成是文件夹创建
                    s.PutNextEntry(entry);
                    s.Flush();
                }


                //先压缩文件，再递归压缩文件夹 
                var filenames = Directory.GetFiles(folderToZip);
                foreach (string file in filenames)
                {
                    FileInfo fl = new FileInfo(file);
                    if (filterExtensions != null && filterExtensions.Contains(fl.Extension))
                    {
                        continue;
                    }

                    //打开压缩文件
                    fs = File.OpenRead(file);

                    byte[] buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);

                    var fileName = Path.GetFileName(file) + "";
                    var folderName = Path.GetFileName(folderToZip);
                    if (!string.IsNullOrEmpty(folderName))
                    {
                        fileName = Path.GetFileName(folderToZip) + "/" + fileName;
                    }

                    var entryPath = Path.Combine(parentFolderName, fileName);
                    entry = new ZipEntry(entryPath)
                    {
                        DateTime = DateTime.Now,
                        Size = fs.Length
                    };
                    fs.Close();

                    crc.Reset();
                    crc.Update(buffer);

                    entry.Crc = crc.Value;

                    s.PutNextEntry(entry);

                    s.Write(buffer, 0, buffer.Length);
                }
            }
            catch
            {
                res = false;
            }
            finally
            {
                fs?.Close();
                if (entry != null)
                {
                    entry = null;
                }
                GC.Collect();
                GC.Collect(1);
            }


            var folders = Directory.GetDirectories(folderToZip);
            foreach (var folder in folders)
            {
                if (!ZipFileDictory(folder, s, Path.Combine(parentFolderName, Path.GetFileName(folderToZip)), filterExtensions))
                {
                    return false;
                }
            }

            return res;
        }

        /**/

        /// <summary>
        /// 压缩目录
        /// </summary>
        /// <param name="folderToZip">待压缩的文件夹，全路径格式</param>
        /// <param name="zipedFile">压缩后的文件名，全路径格式</param>
        /// <param name="password"></param>
        /// <param name="filterExtensions"></param>
        /// <returns></returns>
        private static bool ZipFileDictory(string folderToZip, string zipedFile, string password, string[] filterExtensions)
        {
            if (!Directory.Exists(folderToZip))
            {
                return false;
            }

            ZipOutputStream s = new ZipOutputStream(File.Create(zipedFile));
            s.SetLevel(6);
            if (password != "")
                s.Password = password;

            bool res = ZipFileDictory(folderToZip, s, "", filterExtensions);

            s.Finish();
            s.Close();

            return res;
        }

        /**/
        /// <summary>
        /// 压缩文件
        /// </summary>
        /// <param name="fileToZip">要进行压缩的文件名</param>
        /// <param name="zipedFile">压缩后生成的压缩文件名</param>
        /// <param name="zipPassword">压缩密码</param>
        /// <param name="zipFilters">过滤的文件后缀列表</param>
        /// <returns></returns>
        private static bool ZipFile(string fileToZip, string zipedFile, string zipPassword, string[] zipFilters)
        {
            //如果文件没有找到，则报错
            if (!File.Exists(fileToZip))
            {
                throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
            }
            else
            {
                FileInfo fl = new FileInfo(fileToZip);
                if (zipFilters != null && zipFilters.Contains(fl.Extension))
                {
                    return true;
                }
            }

            //FileStream fs = null;
            FileStream zipFile = null;
            ZipOutputStream zipStream = null;
            ZipEntry zipEntry = null;

            bool res = true;
            try
            {
                zipFile = File.OpenRead(fileToZip);
                byte[] buffer = new byte[zipFile.Length];
                zipFile.Read(buffer, 0, buffer.Length);
                zipFile.Close();

                //
                zipFile = File.Create(zipedFile);
                zipStream = new ZipOutputStream(zipFile);
                if (zipPassword != "")
                    zipStream.Password = zipPassword;
                zipEntry = new ZipEntry(Path.GetFileName(fileToZip));
                zipStream.PutNextEntry(zipEntry);
                zipStream.SetLevel(6);

                zipStream.Write(buffer, 0, buffer.Length);
            }
            catch
            {
                res = false;
            }
            finally
            {
                if (zipEntry != null)
                {
                    zipEntry = null;
                }
                if (zipStream != null)
                {
                    zipStream.Finish();
                    zipStream.Close();
                }
                if (zipFile != null)
                {
                    zipFile.Close();
                    zipFile = null;
                }
                GC.Collect();
                GC.Collect(1);
            }

            return res;
        }

        private static string _dirMain = "";

        /**/
        /// <summary>
        /// 压缩文件 和 文件夹
        /// </summary>
        /// <param name="zipFilesPath">待压缩的文件或文件夹，全路径格式</param>
        /// <param name="toFilePath">压缩后生成的压缩文件名，全路径格式</param>
        /// <param name="zipPassword">压缩密码</param>
        /// <param name="zipFilters">过滤的文件后缀列表</param>
        /// <returns></returns>
        public static bool Zip(string zipFilesPath, string toFilePath, string zipPassword, string[] zipFilters)
        {
            if (Directory.Exists(zipFilesPath))
            {
                if (_dirMain == "")
                    _dirMain = zipFilesPath;
                return ZipFileDictory(zipFilesPath, toFilePath, zipPassword, zipFilters);
            }
            else if (File.Exists(zipFilesPath))
            {
                if (_dirMain == "")
                    _dirMain = "DirFile";
                return ZipFile(zipFilesPath, toFilePath, zipPassword, zipFilters);
            }
            else
            {
                return false;
            }
        }

        #endregion
    }
}
